Skip to content

[DX-154] Fix ably spaces command group#160

Open
sacOO7 wants to merge 6 commits intomainfrom
fix/ably-spaces-command-group-formatting
Open

[DX-154] Fix ably spaces command group#160
sacOO7 wants to merge 6 commits intomainfrom
fix/ably-spaces-command-group-formatting

Conversation

@sacOO7
Copy link
Contributor

@sacOO7 sacOO7 commented Mar 10, 2026

Summary by CodeRabbit

  • New Features

    • Introduced multi-line labeled blocks format for human-readable output with improved clarity and consistency.
  • Bug Fixes

    • Standardized JSON output structure across Spaces commands with domain-key nesting convention.
    • Simplified command flows to reduce unnecessary initial data fetching and live-update subscriptions.
  • Documentation

    • Expanded guidance on output formatting conventions and data structure requirements for Spaces commands.
    • Updated skill documentation with new JSON envelope semantics and non-JSON formatting standards.
  • Refactor

    • Streamlined Spaces command implementations (cursors, locations, locks, members) with cleaner output paths.
    • Migrated from custom type interfaces to direct Ably SDK types for improved maintainability.

@vercel
Copy link

vercel bot commented Mar 10, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
cli-web-cli Ready Ready Preview, Comment Mar 17, 2026 2:29pm

Request Review

@coderabbitai
Copy link

coderabbitai bot commented Mar 10, 2026

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 0902c33c-7170-4fe3-817e-a97447642d0f

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Walkthrough

This pull request refactors Ably Spaces commands to use domain-keyed envelope nesting for JSON and structured multi-line formatting for human-readable output. It introduces a new output utilities module, simplifies multiple command flows by removing complex subscription/monitoring logic, and updates documentation and test expectations to align with these new data-structure conventions.

Changes

Cohort / File(s) Summary
Documentation & Guidance
.claude/skills/ably-codebase-review/SKILL.md, .claude/skills/ably-new-command/SKILL.md, .claude/skills/ably-review/SKILL.md, .claude/skills/ably-new-command/references/patterns.md, AGENTS.md
Updated guidance documents to enforce domain-key nesting for JSON outputs, standardized envelope structure (type, command, success), multi-line labeled-block formatting for non-JSON, and clarified metadata handling and SDK type imports across multiple agent skill files.
Output Infrastructure
src/utils/spaces-output.ts, src/spaces-base-command.ts
Introduced new formatting utility module with JSON and human-readable block formatters for space objects (members, cursors, locks, locations); added state tracking (hasEnteredSpace) to base command to conditionally perform cleanup only when space was entered.
Cursor Commands
src/commands/spaces/cursors/get-all.ts, src/commands/spaces/cursors/set.ts, src/commands/spaces/cursors/subscribe.ts
Simplified cursor-fetching logic by removing live-update subscriptions and space-entry waits; replaced with direct getAll calls; updated output to use new formatCursorBlock/formatCursorOutput formatters; adjusted JSON structure to nest cursors under domain key.
Location Commands
src/commands/spaces/locations/get-all.ts, src/commands/spaces/locations/set.ts, src/commands/spaces/locations/subscribe.ts
Removed complex location retrieval and formatting logic; simplified initialization to use enterSpace: false; replaced detailed metrics output with compact LocationEntry structures; removed duration-based lifecycle and E2E-specific optimization paths; updated to use new location formatters.
Lock Commands
src/commands/spaces/locks/acquire.ts, src/commands/spaces/locks/get-all.ts, src/commands/spaces/locks/get.ts, src/commands/spaces/locks/subscribe.ts
Replaced custom lock detail structures with standard Lock type; removed complex space-connection sequences; simplified output to use formatLockBlock/formatLockOutput; adjusted JSON to nest locks under domain key; removed legacy per-lock detail rendering.
Member Commands
src/commands/spaces/members/enter.ts, src/commands/spaces/members/subscribe.ts
Removed live presence subscriptions and per-member event handling; simplified enter command to direct member entry and output via formatMemberOutput; added duplicate-event suppression and simplified event logging in subscribe; removed extensive per-member update tracking.
Space List Command
src/commands/spaces/list.ts
Removed SpaceMetrics/SpaceStatus interfaces and occupancy metric tracking; simplified JSON output to include only spaceName and channelId; removed detailed formatting helpers.
Test Updates - Unit
test/unit/commands/spaces/cursors/*.test.ts, test/unit/commands/spaces/locations/*.test.ts, test/unit/commands/spaces/locks/*.test.ts, test/unit/commands/spaces/members/*.test.ts, test/unit/commands/spaces/list.test.ts
Updated mock return values from arrays to objects/maps; changed test assertions to reflect new domain-keyed JSON structures, removed expectations for space.enter calls where initialization changed; updated stdout checks to validate new formatter outputs.
Test Updates - E2E & Mocks
test/e2e/spaces/spaces-e2e.test.ts, test/helpers/mock-ably-spaces.ts
Updated E2E test signal checks to match new subscription messages; expanded mock data structures with richer member objects including lastEvent and location fields; changed mock return types from arrays to objects for consistency.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Poem

🐰 The spaces now nest so neatly in their domains,
With envelopes sealed and formatters refined—
No more flat sprawl, just structured refrains,
Each cursor and lock and member aligned! 🎯
The rabbit rejoices at simpler command flows,
And tests that dance with the new patterns that grow. ✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'DX-154 Fix ably spaces command group' clearly identifies the issue being addressed and summarizes the main fix related to the spaces command group formatting and behavior.
Docstring Coverage ✅ Passed Docstring coverage is 83.33% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/ably-spaces-command-group-formatting
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@sacOO7 sacOO7 changed the title Fix ably spaces command group [DX-154] Fix ably spaces command group Mar 10, 2026
@sacOO7 sacOO7 force-pushed the fix/ably-spaces-command-group-formatting branch from 95a65b9 to cd25291 Compare March 13, 2026 09:33
@sacOO7 sacOO7 force-pushed the fix/ably-spaces-command-group-formatting branch from cd25291 to 6a66293 Compare March 16, 2026 09:53
@sacOO7 sacOO7 marked this pull request as ready for review March 17, 2026 08:42
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (8)
.claude/skills/ably-new-command/references/patterns.md (1)

403-412: Consider adding language identifiers to example output blocks.

The static analysis tool flags several fenced code blocks without language specifiers (lines 403, 434, 470, 500). While these are example outputs rather than code, adding text or plaintext as the language identifier would satisfy markdownlint and maintain consistency.

✏️ Suggested fix
-```
+```text
 [2024-01-15T10:30:00.000Z]
 ID: msg-123

Apply similar changes to the other flagged blocks at lines 434, 470, and 500.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.claude/skills/ably-new-command/references/patterns.md around lines 403 -
412, Add a language identifier (e.g., "text" or "plaintext") to the fenced
example output blocks so markdownlint stops flagging them; specifically update
the triple-backtick fences around the sample output that begins with
"[2024-01-15T10:30:00.000Z] ID: msg-123 ..." and apply the same change to the
other similar examples in the file (the blocks starting with the outputs flagged
by the reviewer). Ensure you only modify the opening fence to ```text (or
```plaintext) and leave the block contents unchanged.
src/commands/spaces/cursors/set.ts (1)

171-188: Consider using singular cursor key for single-item result.

Per the JSON Data Nesting Convention in patterns.md (lines 623-630), single-item results from create/get/set operations should use a singular domain key (e.g., { cursor: {...} }), while collections should use plural keys (e.g., { cursors: [...] }).

The current implementation returns { cursors: [{ ... }] } which is technically a collection with one item, but semantically this is setting a single cursor.

♻️ Suggested change for consistency
       if (this.shouldOutputJson(flags)) {
         this.logJsonResult(
           {
-            cursors: [
-              {
-                clientId: this.realtimeClient!.auth.clientId,
-                connectionId: this.realtimeClient!.connection.id,
-                position: (
-                  cursorForOutput as { position: { x: number; y: number } }
-                ).position,
-                data:
-                  (cursorForOutput as { data?: Record<string, unknown> })
-                    .data ?? null,
-              },
-            ],
+            cursor: {
+              clientId: this.realtimeClient!.auth.clientId,
+              connectionId: this.realtimeClient!.connection.id,
+              position: (
+                cursorForOutput as { position: { x: number; y: number } }
+              ).position,
+              data:
+                (cursorForOutput as { data?: Record<string, unknown> })
+                  .data ?? null,
+            },
           },
           flags,
         );
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/commands/spaces/cursors/set.ts` around lines 171 - 188, The JSON output
currently wraps the single set result in a plural array under cursors; change it
to emit a singular cursor object when only one item is returned: inside the
branch that calls this.logJsonResult (the block using
this.shouldOutputJson(flags)), construct and pass { cursor: { clientId:
this.realtimeClient!.auth.clientId, connectionId:
this.realtimeClient!.connection.id, position: (cursorForOutput as { position: {
x: number; y: number } }).position, data: (cursorForOutput as { data?:
Record<string, unknown> }).data ?? null } } instead of { cursors: [ ... ] };
keep the rest of the call (flags) unchanged and only adjust the top-level
key/name used for single-item responses.
src/commands/spaces/locations/get-all.ts (1)

79-82: Use this.logToStderr() for warning output.

Similar to the cursors/get-all command, warning messages should be written to stderr.

Suggested fix
        } else if (entries.length === 0) {
-          this.log(
-            formatWarning("No locations are currently set in this space."),
-          );
+          this.logToStderr(
+            formatWarning("No locations are currently set in this space."),
+          );
         } else {

Based on learnings: "Always use this.logToStderr(formatWarning('...')) for warning messages."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/commands/spaces/locations/get-all.ts` around lines 79 - 82, Replace the
warning that currently uses this.log(formatWarning(...)) with
this.logToStderr(formatWarning(...)) in the branch that handles entries.length
=== 0; locate the else-if that checks entries.length === 0 (where
formatWarning("No locations are currently set in this space.") is used) and call
this.logToStderr with the same formatWarning output instead of this.log.
src/commands/spaces/cursors/get-all.ts (1)

71-72: Use this.logToStderr() for warning output.

Per project conventions, warning messages should be written to stderr rather than stdout to avoid polluting normal output.

Suggested fix
       } else if (cursors.length === 0) {
-        this.log(formatWarning("No active cursors found in space."));
+        this.logToStderr(formatWarning("No active cursors found in space."));
       } else {

Based on learnings: "In the ably/ably-cli TypeScript (oclif) codebase, do not use this.warn() directly in command implementations. Always use this.logToStderr(formatWarning('...')) for warning messages."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/commands/spaces/cursors/get-all.ts` around lines 71 - 72, The warning is
currently being logged to stdout using this.log(formatWarning(...)) in the
get-all command; change it to write to stderr by replacing that call with
this.logToStderr(formatWarning("No active cursors found in space.")) so it
follows the project's convention for warnings in the get-all command
implementation (file: src/commands/spaces/cursors/get-all.ts, locate the branch
where cursors.length === 0 and update the this.log call).
test/unit/commands/spaces/locations/get-all.test.ts (1)

64-69: Assert the serialized coordinates here, not just the key.

toHaveProperty("location") still passes for null or the wrong payload, so this won’t catch regressions in the new map-to-array formatter.

🔎 Tighten the assertion
       expect(resultRecord!.locations[0]).toHaveProperty(
         "connectionId",
         "conn-1",
       );
-      expect(resultRecord!.locations[0]).toHaveProperty("location");
+      expect(resultRecord!.locations[0]).toHaveProperty("location", {
+        x: 100,
+        y: 200,
+      });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/unit/commands/spaces/locations/get-all.test.ts` around lines 64 - 69,
The test currently only asserts that resultRecord.locations[0] has a "location"
property which allows null/wrong values to slip through; update the assertion to
validate the serialized coordinates payload itself (for example assert
resultRecord!.locations[0].location equals the expected coordinates object/array
produced by the map-to-array formatter). Locate the assertion in the
get-all.test.ts where resultRecord!.locations[0] is checked and replace the
toHaveProperty("location") check with a strict equality/assertion against the
expected serialized coordinates (matching the output shape produced by the
map-to-array formatter).
test/unit/commands/spaces/locations/subscribe.test.ts (1)

59-73: Avoid wall-clock sleeps in these unit tests.

These 50ms waits make the assertions timing-dependent. On a busy runner the handler can still be unset, and on a fast exit the update can be emitted after runCommand() has already finished. It’s more deterministic to fire the update from the subscribe mock itself or coordinate with fake timers/a controllable promise.

Also applies to: 100-112

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/unit/commands/spaces/locations/subscribe.test.ts` around lines 59 - 73,
The test uses a 50ms setTimeout to wait for the subscribe handler to be
registered (locationHandler) which makes the test flaky; change the
space.locations.subscribe mockImplementation to synchronously emit the mock
update (i.e., call handler(mockUpdate) inside the mock) or return a controllable
promise that resolves when the handler is set and awaited by the test so
runCommand([... "spaces:locations:subscribe" ...]) sees the update
deterministically; update the logic around the locationHandler variable and the
subscribe mock so the test does not rely on wall-clock sleeps.
test/unit/commands/spaces/locks/subscribe.test.ts (2)

124-126: Use captureJsonLogs() instead of parseNdjsonLines() in this unit test

For mocked unit tests, prefer the repo-standard JSON capture helper to keep assertions consistent with other unit suites and reduce parser-coupling drift.

Suggested update
-import { parseNdjsonLines } from "../../../../helpers/ndjson.js";
+import { captureJsonLogs } from "../../../../helpers/ndjson.js";
...
-      const records = parseNdjsonLines(stdout);
+      const records = captureJsonLogs(stdout);

Based on learnings: In unit tests for this repo, JSON output assertions should use captureJsonLogs() from test/helpers/ndjson.ts rather than manual/parsing-oriented approaches.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/unit/commands/spaces/locks/subscribe.test.ts` around lines 124 - 126,
Replace the manual NDJSON parsing with the repo-standard JSON capture helper:
remove the use of parseNdjsonLines(stdout) in this test and instead call
captureJsonLogs() (imported from test/helpers/ndjson.ts) to obtain the logged
records (await if it returns a promise), then locate the event record the same
way (const eventRecord = records.find((r) => r.type === "event" && r.lock));
also update imports to remove parseNdjsonLines and add captureJsonLogs so
assertions remain consistent with other unit tests.

158-165: Tighten the rejection-path assertion

Consider asserting the error message (or command component) in addition to error being defined; this avoids passing on unrelated failures.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/unit/commands/spaces/locks/subscribe.test.ts` around lines 158 - 165,
The test currently only checks that an error was defined after calling
runCommand for the "spaces:locks:subscribe" command, which can mask unrelated
failures; update the assertion to inspect the error message or command-specific
component returned by runCommand (e.g., check error.message or error.output) to
ensure it contains a distinguishing token such as "spaces:locks:subscribe" or
the space name "test-space". Locate the failing test using runCommand in this
file and replace or add the loose expect(error).toBeDefined() with a tighter
check like expect(error.message).toContain('spaces:locks:subscribe') or
expect(error.message).toContain('test-space') so the test only passes for the
intended rejection path.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/commands/spaces/locks/acquire.ts`:
- Around line 124-130: The JSON output uses an array under "locks" for a
single-item result; change the payload in the acquire command (the block using
this.shouldOutputJson, this.logJsonResult, and formatLockOutput) to return a
singular domain key "lock" with the single formatted lock (e.g., { lock:
formatLockOutput(lock) }) instead of { locks: [...] }; also scan and align the
other single-item commands mentioned (members/enter, cursors/set, locks/get) to
the same singular-key convention to stay consistent with AGENTS.md.

---

Nitpick comments:
In @.claude/skills/ably-new-command/references/patterns.md:
- Around line 403-412: Add a language identifier (e.g., "text" or "plaintext")
to the fenced example output blocks so markdownlint stops flagging them;
specifically update the triple-backtick fences around the sample output that
begins with "[2024-01-15T10:30:00.000Z] ID: msg-123 ..." and apply the same
change to the other similar examples in the file (the blocks starting with the
outputs flagged by the reviewer). Ensure you only modify the opening fence to
```text (or ```plaintext) and leave the block contents unchanged.

In `@src/commands/spaces/cursors/get-all.ts`:
- Around line 71-72: The warning is currently being logged to stdout using
this.log(formatWarning(...)) in the get-all command; change it to write to
stderr by replacing that call with this.logToStderr(formatWarning("No active
cursors found in space.")) so it follows the project's convention for warnings
in the get-all command implementation (file:
src/commands/spaces/cursors/get-all.ts, locate the branch where cursors.length
=== 0 and update the this.log call).

In `@src/commands/spaces/cursors/set.ts`:
- Around line 171-188: The JSON output currently wraps the single set result in
a plural array under cursors; change it to emit a singular cursor object when
only one item is returned: inside the branch that calls this.logJsonResult (the
block using this.shouldOutputJson(flags)), construct and pass { cursor: {
clientId: this.realtimeClient!.auth.clientId, connectionId:
this.realtimeClient!.connection.id, position: (cursorForOutput as { position: {
x: number; y: number } }).position, data: (cursorForOutput as { data?:
Record<string, unknown> }).data ?? null } } instead of { cursors: [ ... ] };
keep the rest of the call (flags) unchanged and only adjust the top-level
key/name used for single-item responses.

In `@src/commands/spaces/locations/get-all.ts`:
- Around line 79-82: Replace the warning that currently uses
this.log(formatWarning(...)) with this.logToStderr(formatWarning(...)) in the
branch that handles entries.length === 0; locate the else-if that checks
entries.length === 0 (where formatWarning("No locations are currently set in
this space.") is used) and call this.logToStderr with the same formatWarning
output instead of this.log.

In `@test/unit/commands/spaces/locations/get-all.test.ts`:
- Around line 64-69: The test currently only asserts that
resultRecord.locations[0] has a "location" property which allows null/wrong
values to slip through; update the assertion to validate the serialized
coordinates payload itself (for example assert
resultRecord!.locations[0].location equals the expected coordinates object/array
produced by the map-to-array formatter). Locate the assertion in the
get-all.test.ts where resultRecord!.locations[0] is checked and replace the
toHaveProperty("location") check with a strict equality/assertion against the
expected serialized coordinates (matching the output shape produced by the
map-to-array formatter).

In `@test/unit/commands/spaces/locations/subscribe.test.ts`:
- Around line 59-73: The test uses a 50ms setTimeout to wait for the subscribe
handler to be registered (locationHandler) which makes the test flaky; change
the space.locations.subscribe mockImplementation to synchronously emit the mock
update (i.e., call handler(mockUpdate) inside the mock) or return a controllable
promise that resolves when the handler is set and awaited by the test so
runCommand([... "spaces:locations:subscribe" ...]) sees the update
deterministically; update the logic around the locationHandler variable and the
subscribe mock so the test does not rely on wall-clock sleeps.

In `@test/unit/commands/spaces/locks/subscribe.test.ts`:
- Around line 124-126: Replace the manual NDJSON parsing with the repo-standard
JSON capture helper: remove the use of parseNdjsonLines(stdout) in this test and
instead call captureJsonLogs() (imported from test/helpers/ndjson.ts) to obtain
the logged records (await if it returns a promise), then locate the event record
the same way (const eventRecord = records.find((r) => r.type === "event" &&
r.lock)); also update imports to remove parseNdjsonLines and add captureJsonLogs
so assertions remain consistent with other unit tests.
- Around line 158-165: The test currently only checks that an error was defined
after calling runCommand for the "spaces:locks:subscribe" command, which can
mask unrelated failures; update the assertion to inspect the error message or
command-specific component returned by runCommand (e.g., check error.message or
error.output) to ensure it contains a distinguishing token such as
"spaces:locks:subscribe" or the space name "test-space". Locate the failing test
using runCommand in this file and replace or add the loose
expect(error).toBeDefined() with a tighter check like
expect(error.message).toContain('spaces:locks:subscribe') or
expect(error.message).toContain('test-space') so the test only passes for the
intended rejection path.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 262c13ff-538d-44de-91a0-c868d4aa7448

📥 Commits

Reviewing files that changed from the base of the PR and between ad9f0a5 and 563d187.

📒 Files selected for processing (35)
  • .claude/skills/ably-codebase-review/SKILL.md
  • .claude/skills/ably-new-command/SKILL.md
  • .claude/skills/ably-new-command/references/patterns.md
  • .claude/skills/ably-review/SKILL.md
  • AGENTS.md
  • src/commands/spaces/cursors/get-all.ts
  • src/commands/spaces/cursors/set.ts
  • src/commands/spaces/cursors/subscribe.ts
  • src/commands/spaces/list.ts
  • src/commands/spaces/locations/get-all.ts
  • src/commands/spaces/locations/set.ts
  • src/commands/spaces/locations/subscribe.ts
  • src/commands/spaces/locks/acquire.ts
  • src/commands/spaces/locks/get-all.ts
  • src/commands/spaces/locks/get.ts
  • src/commands/spaces/locks/subscribe.ts
  • src/commands/spaces/members/enter.ts
  • src/commands/spaces/members/subscribe.ts
  • src/spaces-base-command.ts
  • src/utils/spaces-output.ts
  • test/e2e/spaces/spaces-e2e.test.ts
  • test/helpers/mock-ably-spaces.ts
  • test/unit/commands/spaces/cursors/get-all.test.ts
  • test/unit/commands/spaces/cursors/set.test.ts
  • test/unit/commands/spaces/cursors/subscribe.test.ts
  • test/unit/commands/spaces/list.test.ts
  • test/unit/commands/spaces/locations/get-all.test.ts
  • test/unit/commands/spaces/locations/set.test.ts
  • test/unit/commands/spaces/locations/subscribe.test.ts
  • test/unit/commands/spaces/locks/acquire.test.ts
  • test/unit/commands/spaces/locks/get-all.test.ts
  • test/unit/commands/spaces/locks/get.test.ts
  • test/unit/commands/spaces/locks/subscribe.test.ts
  • test/unit/commands/spaces/members/enter.test.ts
  • test/unit/commands/spaces/members/subscribe.test.ts
💤 Files with no reviewable changes (1)
  • test/unit/commands/spaces/list.test.ts

Comment on lines 124 to 130
if (this.shouldOutputJson(flags)) {
this.logJsonResult({ lock: lockDetails }, flags);
this.logJsonResult({ locks: [formatLockOutput(lock)] }, flags);
} else {
this.log(formatSuccess(`Lock acquired: ${formatResource(lockId)}.`));
this.log(
`${formatLabel("Lock details")} ${this.formatJsonOutput(lockDetails, { ...flags, "pretty-json": true })}`,
);
this.log(formatLockBlock(lock));
this.log(`\n${formatListening("Holding lock.")}`);
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check how other single-item spaces commands structure their JSON output
rg -n 'logJsonResult.*{' src/commands/spaces/ -A1 | head -40

Repository: ably/ably-cli

Length of output: 152


🏁 Script executed:

#!/bin/bash
# Check how spaces commands structure their JSON output - fixed regex
rg 'logJsonResult' src/commands/spaces/ -A1 -B1

Repository: ably/ably-cli

Length of output: 2215


🏁 Script executed:

#!/bin/bash
# Get full context for cursors/set.ts
echo "=== cursors/set.ts ==="
sed -n '1,200p' src/commands/spaces/cursors/set.ts | grep -A5 'logJsonResult'

echo -e "\n=== spaces/list.ts ==="
sed -n '1,200p' src/commands/spaces/list.ts | grep -A5 'logJsonResult'

echo -e "\n=== Check for SKILL.md and AGENTS.md ==="
find . -maxdepth 3 -name 'SKILL.md' -o -name 'AGENTS.md'

Repository: ably/ably-cli

Length of output: 539


🏁 Script executed:

#!/bin/bash
# Check AGENTS.md for domain key convention
echo "=== AGENTS.md (first 300 lines) ==="
head -300 AGENTS.md

echo -e "\n=== locations/set.ts full logJsonResult context ==="
rg -B2 -A10 'logJsonResult.*location' src/commands/spaces/locations/set.ts

Repository: ably/ably-cli

Length of output: 18966


Implement singular domain key for single-item results.

Per the documented convention in AGENTS.md ("Events and single results: nest under a singular domain key"), the acquire command should return a single lock unwrapped under a singular key:

-          this.logJsonResult({ locks: [formatLockOutput(lock)] }, flags);
+          this.logJsonResult({ lock: formatLockOutput(lock) }, flags);

Note: This convention is enforced correctly in locations/set.ts but violated in several other single-item commands (members/enter, cursors/set, and locks/get). Consider standardizing across the codebase.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/commands/spaces/locks/acquire.ts` around lines 124 - 130, The JSON output
uses an array under "locks" for a single-item result; change the payload in the
acquire command (the block using this.shouldOutputJson, this.logJsonResult, and
formatLockOutput) to return a singular domain key "lock" with the single
formatted lock (e.g., { lock: formatLockOutput(lock) }) instead of { locks:
[...] }; also scan and align the other single-item commands mentioned
(members/enter, cursors/set, locks/get) to the same singular-key convention to
stay consistent with AGENTS.md.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant